#include <iostream>
#include <string>
#include <unistd.h>
#include <cassert>
#include <vector>
#include <sys/wait.h>
#include <sys/types.h>
#include "task.hpp"
using namespace std;

const int gnum = 5;
//定义一个全局任务对象t
Task t;

class EndPoint{
private:
    static int number;

public:
    pid_t _child_id;
    int _write_fd;
    string _process_name;

public:
    EndPoint(int id, int fd)
        : _child_id(id)
        , _write_fd(fd){
        //process-0[pid:fd]
        char namebuffer[64];
        snprintf(namebuffer, sizeof(namebuffer), "child_process-%d[%d:%d]", _write_fd - 3, _child_id, _write_fd);
        _process_name = namebuffer;
    }

    string name() const{
        return _process_name;
    }

    ~EndPoint(){

    }
};
int EndPoint::number = 0;

//子进程要执行的方法
void WaitCommand(){

    while(1){
        int command = 0;
        //从管道（标准输入流）中读取内容存放到command中
        int n = read(0, &command, sizeof(int));
        if(n == sizeof(int)){
            //读取成功，执行相应任务
            t.Execute(command);
        }else if(n == 0){
            cout << "3.exit  pid: " << getpid() << endl;
            break;
        }else{
            break;
        }
    }

}

void createProcess(vector<EndPoint> *end_points){
        
    //用个vector存放父进程所创建的管道
    vector<int> fds;

    for (int i = 0; i < gnum;i++){
        //1.1、定义管道
        int pipefd[2] = {0};
        int n = pipe(pipefd);
        assert(n == 0);
        

        //1.2、创建子进程
        pid_t id = fork();
        assert(id != -1);
        if (id == 0){
            //子进程
            //1.3、关闭不需要的fd
            //关闭与父进程的写端
            close(pipefd[1]);
            //关闭从父进程中继承下来的写端
            for(auto &fd : fds){
                close(fd);
            }
            //1.3.1、输入重定向 -- 用管道读端覆盖标准输入流：我们期望所有的子进程读取指令的时候都从标准输入读取
            dup2(pipefd[0], 0);
            //1.3.2、子进程开始等待获取命令
            WaitCommand();

            close(pipefd[0]);
            exit(0);
        }

        //父进程
        //1.3、关闭不需要的fd
        close(pipefd[0]);
        
        //1.4、将新的子进程和父进程的管道写端构建对象   --  用pipefd[1]向id中写入数据
        end_points->push_back(EndPoint(id, pipefd[1]));

        //每次完成管道设置后都把父进程的写端存入vector
        fds.push_back(pipefd[1]);
    }
}

int ShowBoard(){
    cout << "〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓" << endl;
    cout << "   0. 执行LOG        1. 执行SQL" << endl;
    cout << "   2. 执行REQUESt    3. EXIT" << endl;
    cout << "〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓" << endl;
    cout << "请选择：" << endl;
    int command = 0;
    cin >> command;
    return command;
}

void ctrlProcess(const vector<EndPoint>& end_points){
     int num = 0;

    // // 自动化的
    // while (1){
    //     // 2.1、选择任务
    //     int command = LOG;

    //     // 2.2、选择进程
    //     //随机选择一个子进程执行已选任务
    //     int index = rand() % end_points.size();

    //     // 2.3、下发任务
    //     //    子进程的fd                   已选择的任务（目标缓冲区）
    //     write(end_points[index]._write_fd, &command, sizeof(command));  //写到fd的标准输入流中
    //     sleep(1);
        
    // }

    // 交互式的
    while(1){
        // 2.1、选择任务
        int command = ShowBoard();
        if(command<0 || command>3){
            cout << "输入错误，请重新输入！" << endl;
        }
        if(command == 3){
            break;
        }

        // 2.2、选择进程
        // 按类型选择子进程，直接用command
        cout << "选择的进程是：" << end_points[command].name() << "  处理任务：" << command << endl;
        // 2.3、下发任务
        write(end_points[command]._write_fd, &command, sizeof(command));
        sleep(1);
    }
    
}

void waitProcess(const vector<EndPoint>& end_pionts){
    // // 1.让子进程全部退出   --  关闭父进程的所有_write_fd
    // for (const auto &ep : end_pionts){
    //     close(ep._write_fd);
    // }
    // cout << "子进程退出完毕" << endl;

    // sleep(2);
    // // 2.父进程回收子进程的僵尸状态
    // for (const auto &ep : end_pionts){
    //     waitpid(ep._child_id, nullptr, 0);
    // }
    // cout << "子进程回收完毕" << endl;

    //不能写在一个循环中：在创建管道的时候，第一次创建子进程只有一个管道，但是之后创建子进程时后续的子进程会继承父进程的管道，却只对自己的管道进行修改
    //除非倒序退出子进程
    //解决子进程继承父进程的管道：用一个vector存放父进程所开辟的管道，让这个vector被子进程继承，在子进程中手动关闭这些管道
    for(const auto &ep : end_pionts){
        close(ep._write_fd);
        cout << "关闭子进程: " << ep._process_name << endl;
        waitpid(ep._child_id, nullptr, 0);
        cout << "子进程回收完毕" << endl;
        cout << endl;
    }
}

int main(){
    // 1、构建控制结构   --  父写子读
    vector<EndPoint> end_points;
    createProcess(&end_points);

    // 2、进行控制
    ctrlProcess(end_points);
    

    // 3.处理所有退出问题
    waitProcess(end_points);

    //
    return 0;
}